﻿/*
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * See product pages for more information.
 * http://closesudoku.codeplex.com
 * Copyright (C), 2010, hrondra
 */
using System;

using System.Collections.Generic;
using System.Text;
using Fluid.Controls;
using System.Drawing;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using Close;
using Close.Data;
using Close.Controls;
using Close.Classes;
using Close.controls.GamePads;
using Close.Properties;
using Close.controls;
using Close.controls.FFluid;

namespace Close.Controls
{
    public class MainGui : FluidPanel
    {
        private BottomNumericPad keyPad;
        private StopWatch _oneSecondTimer;
        private ushort _currentGameId;         

        ThemeClass themeClass = ThemeClass.Settings;
        private ThemeClass.DisplayModes _gameMode;
        private Popup popup;

        public MainGui(ushort gameId)
        {
            //------------------------------ grid ---------------------------------------------------
            const int minGap = 1;
            int gridHeight = themeClass.DisplayHeight - themeClass.ButtonsPanelHeight;
            int gridWidth = themeClass.DisplayWidth;

            _currentGameId = gameId;
            int rowBetweenGap = ((themeClass.DisplayHeight - themeClass.ButtonsPanelHeight) % 9) > minGap ? ((themeClass.DisplayHeight - themeClass.ButtonsPanelHeight) % 9) / 2 : minGap;
            int colBetweenGap = (themeClass.DisplayWidth % 9) > minGap ? (themeClass.DisplayWidth % 9) / 2 : minGap;

            if (((themeClass.DisplayHeight - themeClass.ButtonsPanelHeight / 2) % 9) == 0)
                gridHeight -= 2 * minGap;
            if ((themeClass.DisplayWidth % 9) == 0)
                gridWidth -= 2 * minGap;

            int rowBetweenGapCurrent = 0, colBetweenGapCurrent = 0;
            //load historical data
            GameData.SavedGameRow savedGame = GameDatabase.GamesData.LoadValues(gameId);
            for (int row = 0; row < 9; row++)
            {
                colBetweenGapCurrent = 0;
                for (int column = 0; column < 9; column++)
                {
                    GridButton btn = new GridButton();//row.ToString() + column.ToString()

                    byte imprintedValue = 0;
                    if ((imprintedValue = GameDatabase.GamesData.GetImprintedValue(gameId, row, column)) != '0')
                    {
                        btn.ImprintedValue = (char)imprintedValue; //(char)((byte)'0' + imprintedValue);
                        btn.BackColor = themeClass.BoardBackgroundImprinted;
                    }
                    //#if Debug
                    //else if(_currentGameId == 3)
                    //{
                    //}
                    //#endif
                    else
                    {
                        if (savedGame != null)
                        {
                            btn.SetFaceValueWithoutHistory(savedGame.getValue(row, column));//savedGame.
                            if (btn.FaceValue == '0')
                                btn.SetFaceValueWithoutHistory(' ');
                            short allNotesInInt = savedGame.GetNotes(row, column);
                            if (allNotesInInt != 0)
                                btn.AllNotesInInt = allNotesInInt;
                        }
                        else
                            btn.SetFaceValueWithoutHistory(' ');
                        btn.BackColor = themeClass.BoardBackground;
                    }

                    btn.EnableCache = false;
                    btn.Name = "policko" + row.ToString() + column.ToString();
                    btn.Width = gridWidth / 9;
                    btn.Height = gridHeight / 9;
                    btn.Top = gridHeight / 9 * row + rowBetweenGapCurrent;
                    btn.Left = gridWidth / 9 * column + colBetweenGapCurrent;
                    
                    btn.Bounds = new Rectangle(btn.Left, btn.Top, btn.Width, btn.Height);

                    //rozdeleni do oblasti
                    if (column == 2 || column == 5)
                        colBetweenGapCurrent += colBetweenGap;
                    if ((column == 8 && row == 2) || (column == 8 && row == 5))
                        rowBetweenGapCurrent += rowBetweenGap;

                    //nastaveni krize
                    btn.MouseDown += new EventHandler(btn_PolickoButtonEntered);
                    btn.MouseUp += new EventHandler(btn_PolickoButtonLeaved);
                    btn.Click += new EventHandler(btn_PolickoButtonClicked);
                    Controls.Add(btn);
                }
            }
            if (savedGame == null)
            {
                _oneSecondTimer = new StopWatch(savedGame == null ? 0 : savedGame.playedtime, true);
            }
            else
            {
                _oneSecondTimer = new StopWatch(savedGame == null ? 0 : savedGame.playedtime, (savedGame.state != (byte)GameStates.Completed));
                _isSolved = (savedGame.state == (byte)GameStates.Completed);
            }
            RevertingChanges.UndoRedoHistory.Clear(gameId);
        }
        

        protected override void InitControl()
        {
             base.InitControl();

            polickoClicked += WriteToPolicko;

            BottomNumericPad numsPad = new BottomNumericPad(0, themeClass.DisplayHeight - themeClass.ButtonsPanelHeight, themeClass.DisplayWidth - 1 - ThemeClass.Settings.PopMenuSmallWidth, themeClass.ButtonsPanelHeight);
            keyPad = numsPad;
            numsPad.EnableDoubleBuffer = false;
            numsPad.Bounds = new Rectangle(1, themeClass.DisplayHeight - themeClass.ButtonsPanelHeight, themeClass.DisplayWidth - ThemeClass.Settings.PopMenuSmallWidth, themeClass.ButtonsPanelHeight);
            numsPad.ChangeModeClicked += new EventHandler(numsPad_ChangeModeClicked);
            Controls.Add(numsPad);
            _gameMode = ThemeClass.Settings.DisplayMode;
            keyPad.SwitchToMode(_gameMode == ThemeClass.DisplayModes.SingleNumber || _gameMode == ThemeClass.DisplayModes.Mixed);
            FlatButton btnShowMenu = new FlatButton("<");
            btnShowMenu.Bounds = new Rectangle(themeClass.DisplayWidth - ThemeClass.Settings.PopMenuSmallWidth, themeClass.DisplayHeight - themeClass.ButtonsPanelHeight, ThemeClass.Settings.PopMenuSmallWidth, themeClass.PopMenuHeight);
            btnShowMenu.BackColor = ThemeClass.Settings.NumpadBackroundColor;
            btnShowMenu.ForeColor = ThemeClass.Settings.NumpadForeColor;
            btnShowMenu.Click += new EventHandler(btnShowMenu_Click);
            Controls.Add(btnShowMenu);

            int popupWidth = (int) (.8 * themeClass.DisplayWidth);
            popup = new Popup((int)(.1 * themeClass.DisplayWidth), (int)(.1 * themeClass.DisplayHeight), (int)(.8 * themeClass.DisplayWidth), (int) (.8 * themeClass.DisplayHeight));
            popup.ButtonClick += new Popup.PopupButtonDelegate(popup_ButtonClick);
            Controls.Add(popup);
            popup.Visible = false;
        }

        void popup_ButtonClick(Popup sender, FluidButton btn)
        {
            if (sender.wasChanged)
            {
                ChangeBackground(sender.ButtonInGrid);
                if (issolved())
                {
                    ShowSolvedDialog();
                    _oneSecondTimer.Stop();
                }
                SaveGame(); //TODO: all save game to one place
            }
            sender.Hide();
            

        }

        /// <summary>
        /// Game solved - show congratulation dialog.
        /// </summary>
        private void ShowSolvedDialog()
        {
            MessageBox.Show(Resources.Congratulation);
        }

        private void numsPad_ChangeModeClicked(object sender, EventArgs e)
        {
            if (_gameMode == ThemeClass.DisplayModes.Popup)
                _gameMode = ThemeClass.DisplayModes.SingleNumber;
            else
                _gameMode = ThemeClass.DisplayModes.Popup;
            keyPad.SwitchToMode(_gameMode == ThemeClass.DisplayModes.SingleNumber || _gameMode == ThemeClass.DisplayModes.Mixed);
        }

        private MenuInGame mig;


        /// <summary>
        /// Show in game menu
        /// </summary>
        /// <param name="sender">button</param>
        void btnShowMenu_Click(object sender, EventArgs e)
        {
            FlatButton btn = (FlatButton)sender;
            
            if(mig == null)
            {
                mig = new MenuInGame(0, themeClass.DisplayHeight - themeClass.ButtonsPanelHeight, themeClass.DisplayWidth - 1 - ThemeClass.Settings.PopMenuSmallWidth, themeClass.ButtonsPanelHeight, _oneSecondTimer);
                mig.EnableDoubleBuffer = false;
                mig.BackColor = Color.Gray;
                mig.ExitClick += new EventHandler(mig_ExitClick);
                mig.RestartClick += new EventHandler(mig_RestartClick);
                mig.ConfigurationClick += new EventHandler(mig_ConfigurationClick);
                mig.UndoEvent += new MenuInGame.UndoRedoDelegate(mig_Undo);
                mig.RedoEvent += new MenuInGame.UndoRedoDelegate(mig_Redo);
                RevertingChanges.UndoRedoHistory.StepChanged += new RevertingChanges.UndoChangeRedoDelagate(UndoRedoHistory_StepChanged);

                mig.Visible = false;
            }


            if (mig.Visible = !mig.Visible)
            {
                mig.Show();
                mig.Invalidate();
                btn.Text = ">";
            }
            else
            {
                mig.Hide();
                btn.Text = "<";
            }
            
        }

        void UndoRedoHistory_StepChanged()
        {
            mig.refreshUndoRedoImages();
        }

        /// <summary>
        /// Restart the game - reset counter, delete values and notes.
        /// </summary>
        void mig_RestartClick(object sender, EventArgs e)
        {
            RestartTheGame();
            _oneSecondTimer.Reset();
            
        }

        void mig_ConfigurationClick(object sender, EventArgs e)
        {
            SettingsDialog.Show();
        }

        void mig_Undo(RevertingChanges.SudokuBoardAction e)
        {
            mig_UndoRedo(e, true);
        }

        void mig_Redo(RevertingChanges.SudokuBoardAction e)
        {
            mig_UndoRedo(e, false);
        }

        void mig_UndoRedo(RevertingChanges.SudokuBoardAction e, bool undo)
        {
            foreach (FluidControl flc in this.GetControls())
            {
                if (!String.IsNullOrEmpty(flc.Name))
                {
                    if (flc.Name.IndexOf("policko" + e.rowColumn.ToString().PadLeft(2,'0')) > -1)
                    {
                        GridButton btn2 = (GridButton)flc;
                        btn2.SetValueFromUndoRedo(e.rowColumn, e.stepValue, btn2.FaceValue, btn2.AllNotesInInt, undo);
                        btn2.Invalidate();
                        break;
                    }
                }
            }
        }

        /// <summary>
        /// Exit game was clicked.
        /// </summary>
        public event EventHandler ExitClick;

        /// <summary>
        /// Exit game was clicked.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void mig_ExitClick(object sender, EventArgs e)
        {
            SaveGame();
            if (ExitClick != null)
                ExitClick(this, e);
        }

        protected override void OnSizeChanged(Size oldSize, Size newSize)
        {
            base.OnSizeChanged(oldSize, newSize);
            int w = this.Width;

        }

        private const bool _secondSelectIsDelete = true;
        private void WriteToPolicko(object sender, EventArgs e)
        {
            GridButton btn = (GridButton)sender;
            if (_gameMode == ThemeClass.DisplayModes.SingleNumber || (_gameMode == ThemeClass.DisplayModes.Mixed && keyPad.AnythingSelected))
            {
                if (keyPad.IsNoteMode)
                {
                    if (keyPad.SelectedValue == 'C')
                    {
                        btn.SetNote(0);
                        ChangeBackground(btn);
                    }
                    else if (keyPad.SelectedValue != ' ')
                    {
                        btn.SetNote((byte)keyPad.SelectedValue - '0');
                        ChangeBackground(btn);
                    }
                }
                else
                {
                    if (keyPad.SelectedValue == 'C' || (_secondSelectIsDelete && keyPad.SelectedValue == btn.FaceValue))
                    {
                        btn.FaceValue = ' ';
                        ChangeBackground(btn);
                    }
                    else if (keyPad.SelectedValue != ' ')
                    {
                        btn.FaceValue = keyPad.SelectedValue;
                        ChangeBackground(btn);
                    }
                    if (issolved())
                    {
                        ShowSolvedDialog();
                        _oneSecondTimer.Stop();
                    }
                }
            }
            else
            {
                popup.Show(btn);
                popup.Visible = true;
            }

        }

        #region "grid"

        private GridButton previousButton;
        private void ChangeBackground(GridButton btn)
        {
            if (!btn.Imprinted)
            {
                if (previousButton != null)
                    previousButton.BackColor = themeClass.BoardBackground;
                btn.BackColor = ThemeClass.Settings.BoardBackgroundLast;
                previousButton = btn;
            }
        }

        /// <summary>
        /// Occurs when a button was clicked - create row/line highlighting.
        /// </summary>
        void btn_PolickoButtonEntered(object sender, EventArgs e)
        {
            FluidButton btn = (FluidButton)sender;
            string clRow  = btn.Name.Substring(btn.Name.Length - 2, 1);
            string clCol= btn.Name.Substring(btn.Name.Length - 1, 1);

            Regex reg = new Regex("policko[0-9]{1}" + clCol);

            int colQuadrant = int.Parse(clCol) / 3;
            int rowQuadrant = int.Parse(clRow) / 3;

            //test to show conflicts number in quadrant
            foreach(FluidControl flc in this.GetControls())
            {
                if (!String.IsNullOrEmpty(flc.Name))
                {
                    if (flc.Name.IndexOf("policko" + clRow) >= 0)
                    {
                        GridButton btn2 = (GridButton)flc;
                        if (btn2.FaceValue == keyPad.SelectedValue)
                        {
                            btn2.ConflictNumber = true;
                            btn2.Invalidate();
                        }
                        btn2.BackColor = themeClass.BoardBackgroundSelected;
                    }                    
                    if (reg.IsMatch(flc.Name))
                    {
                        GridButton btn2 = (GridButton)flc;
                        if (btn2.FaceValue == keyPad.SelectedValue)
                        {
                            btn2.ConflictNumber = true;
                            btn2.Invalidate();
                        }
                        btn2.BackColor = themeClass.BoardBackgroundSelected;
                    }
                    //test to show conflicts number in quadrant
                    if (flc.Name.IndexOf("policko") >= 0)
                    {
                        int iRow = int.Parse(flc.Name.Substring(flc.Name.Length - 2, 1)), iCol = int.Parse(flc.Name.Substring(flc.Name.Length - 1, 1));
                        if ((colQuadrant * 3 <= iCol && iCol < (colQuadrant * 3 + 3))
                            && (rowQuadrant * 3 <= iRow && iRow < rowQuadrant * 3 + 3))
                        {
                            GridButton btn2 = (GridButton)flc;
                            if (btn2.FaceValue == keyPad.SelectedValue)
                            {
                                btn2.ConflictNumber = true;
                                btn2.Invalidate();
                            }
                        }
                    }
                }
            }

        }

        /// <summary>
        /// Occurs when a button click is finished - cancel row/line highlighting.
        /// </summary>
        void btn_PolickoButtonLeaved(object sender, EventArgs e)
        {
            FluidButton btn = (FluidButton)sender;
            string clRow = btn.Name.Substring(btn.Name.Length - 2, 1);
            string clCol = btn.Name.Substring(btn.Name.Length - 1, 1);

            Regex reg = new Regex("policko[0-9]{1}" + clCol);

            int colQuadrant = int.Parse(clCol) / 3;
            int rowQuadrant = int.Parse(clRow) / 3;


            foreach (FluidControl flc in this.GetControls())
            {
                if (!String.IsNullOrEmpty(flc.Name))
                {
                    if (flc.Name.IndexOf("policko" + clRow) >= 0)
                    {
                        GridButton btn2 = (GridButton)flc;
                        btn2.BackColor = (btn2.Imprinted) ? themeClass.BoardBackgroundImprinted : themeClass.BoardBackground;
                        btn2.ConflictNumber = false;
                    }
                    if (reg.IsMatch(flc.Name))
                    {
                        GridButton btn2 = (GridButton)flc;
                        btn2.BackColor = (btn2.Imprinted) ? themeClass.BoardBackgroundImprinted : themeClass.BoardBackground;
                        btn2.ConflictNumber = false;
                    }

                    if (flc.Name.IndexOf("policko") >= 0)
                    {
                        int iRow = int.Parse(flc.Name.Substring(flc.Name.Length - 2, 1)), iCol = int.Parse(flc.Name.Substring(flc.Name.Length - 1, 1));
                        if ((colQuadrant * 3 <= iCol && iCol< (colQuadrant * 3 + 3))
                            && (rowQuadrant * 3 <= iRow && iRow  < rowQuadrant * 3 + 3))
                        {
                            GridButton btn2 = (GridButton)flc;
                            btn2.ConflictNumber = false;
                            btn2.Invalidate();
                        }
                    }                    
                }
            }
        }

        public EventHandler polickoClicked;
        void btn_PolickoButtonClicked(object sender, EventArgs e)
        {
            if (! issolved())
            {
                if (polickoClicked != null)
                    polickoClicked(sender, e);
                SaveGame();
            }
        }

        private bool _isSolved = false;
        /// <summary>
        /// Is it completed?
        /// </summary>
        /// <returns>solved/not</returns>
        private bool issolved()
        {
            if (_isSolved)
                return true;

            char[,] allButtonsValues = new char[9, 9];
            foreach (FluidControl flc in this.GetControls())
            {
                if (!String.IsNullOrEmpty(flc.Name))
                {
                    if (flc.Name.IndexOf("policko") >= 0)
                    {
                        GridButton btn2 = (GridButton)flc;
                        if (btn2.FaceValue > '9' || btn2.FaceValue < '1')
                            return false;//all values are not selected
                        allButtonsValues[int.Parse(flc.Name.Substring(7, 1)), int.Parse(flc.Name.Substring(8, 1))] = btn2.FaceValue;
                    }
                }
            }

            List<char>[,] vlsArea1 = new List<char>[3,3];
            vlsArea1[0, 0] = new List<char>(10);
            vlsArea1[0, 1] = new List<char>(10);
            vlsArea1[0, 2] = new List<char>(10);
            vlsArea1[1, 0] = new List<char>(10);
            vlsArea1[1, 1] = new List<char>(10);
            vlsArea1[1, 2] = new List<char>(10);
            vlsArea1[2, 0] = new List<char>(10);
            vlsArea1[2, 1] = new List<char>(10);
            vlsArea1[2, 2] = new List<char>(10);

            for (int r = 0; r < 9; r++)
            {
                List<char> vlsRows = new List<char>(10);
                List<char> vlsCol = new List<char>(10);
                for (int s = 0; s < 9; s++)
                {
                    //duplicity in rows
                    if (vlsRows.Contains(allButtonsValues[r, s]))
                        return false;
                    else
                        vlsRows.Add(allButtonsValues[r, s]);

                    //duplicity in columns
                    if (vlsCol.Contains(allButtonsValues[s, r]))
                        return false;
                    else
                        vlsCol.Add(allButtonsValues[s, r]);
                    if (vlsArea1[r / 3, s / 3].Contains(allButtonsValues[r, s]))
                        return false;
                    else
                        vlsArea1[r / 3, s / 3].Add(allButtonsValues[r, s]);
                }

            }
            _isSolved = true;
            return true;
        }

        /// <summary>
        /// Save all the values in a file.
        /// </summary>
        void SaveGame()
        {
            char[] allButtonsValues = new char[81];
            Int16[] allButtonsNotes = new Int16[81];
            foreach (FluidControl flc in this.GetControls())
            {
                if (!String.IsNullOrEmpty(flc.Name))
                {
                    if (flc.Name.IndexOf("policko") >= 0)
                    {
                        GridButton btn2 = (GridButton)flc;
                        int index = int.Parse(flc.Name.Substring(7, 1)) * 9 + int.Parse(flc.Name.Substring(8, 1));
                        allButtonsValues[index] = btn2.FaceValue;
                        allButtonsNotes[index] = btn2.AllNotesInInt;
                    }
                }
            }
            if (_isSolved)
            {
                GameDatabase.GamesData.SaveGame(_currentGameId, new string(allButtonsValues).Replace(" ", "0"), allButtonsNotes, _oneSecondTimer.ElapsedTime, GameStates.Completed);
            }
            else
            {
                GameDatabase.GamesData.SaveGame(_currentGameId, new string(allButtonsValues).Replace(" ", "0"), allButtonsNotes, _oneSecondTimer.ElapsedTime, GameStates.InProgess);
            }
            
        }

        /// <summary>
        /// Restart the game.
        /// </summary>
        /// <remarks>Values are not saved at the moment.</remarks>
        void RestartTheGame()
        {
            foreach (FluidControl flc in this.GetControls())
            {
                if (!String.IsNullOrEmpty(flc.Name))
                {
                    if (flc.Name.IndexOf("policko") >= 0)
                    {
                        GridButton btn2 = (GridButton)flc;
                        int index = int.Parse(flc.Name.Substring(7, 1)) * 9 + int.Parse(flc.Name.Substring(8, 1));
                        btn2.FaceValue = ' ';
                        btn2.SetNote(0);
                    }
                }
            }
            _isSolved = false;
            RevertingChanges.UndoRedoHistory.Clear();
        }

        #endregion

    }
}